package com.zx.idc.backend.gui.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zx.idc.backend.gui.common.Result;
import com.zx.idc.backend.gui.common.util.AjaxUtil;
import com.zx.idc.backend.gui.common.util.NetworkUtil;
import com.zx.idc.common.util.ProcessUtil;
import com.zx.idc.ds.sys.entity.SysUser;
import com.zx.idc.ds.tlog.entity.TLogSecurity;
import com.zx.idc.ds.tlog.service.ITLogSecurityService;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.util.NestedServletException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.util.Objects;


/**
 * 权限过滤器, 对于没有授予访问权限的资源返回401
 * @auth jinyuanf
 * @date 2019/4/28 11:45
 **/
@Service
public class PermissionsAuthorizationFilter extends AuthorizationFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsAuthorizationFilter.class);
    private ITLogSecurityService itLogSecurityService;
    private TokenManagerUtil tokenManagerUtil;

    public PermissionsAuthorizationFilter(ITLogSecurityService itLogSecurityService,TokenManagerUtil tokenManagerUtil) {
        this.itLogSecurityService = itLogSecurityService;
        this.tokenManagerUtil = tokenManagerUtil;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {
        Subject subject = getSubject(servletRequest, servletResponse);
        String[] perms = (String[]) mappedValue;
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!subject.isPermitted(perms[0])) {
                    isPermitted = false;
                }
            } else {
                if (!subject.isPermittedAll(perms)) {
                    isPermitted = false;
                }
            }
        }

        return isPermitted;
    }

    /**
     * 验证失败调用
     *
     * @param srequest
     * @param sresponse
     * @param mappedValue
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest srequest, ServletResponse sresponse, Object mappedValue) throws Exception {
        HttpServletRequest request = (HttpServletRequest) srequest;
        HttpServletResponse response = (HttpServletResponse) sresponse;
        //ajax则返回json
        int userId = 0;
        try{
            final SysUser currentUser = tokenManagerUtil.getCurrentUser();
            if(!Objects.isNull(currentUser)){
                userId = currentUser.getId();
            }
        }catch (Exception ignored){
        }
        
        itLogSecurityService.insert(new TLogSecurity(TLogSecurity.OVERPOWER, userId, NetworkUtil.getIpAddress(request), LocalDateTime.now(), "越权", "对不起，没有授权", ProcessUtil.getPid(), null));
        if (AjaxUtil.isAjax(request)) {
            Result result = new Result(Result.UN_AUTHORIZED, "对不起，没有授权", null);
            writeJsonError(response, result);
        } else {//设置授权失败拦截页面
            super.setUnauthorizedUrl("/common/forward?pagePath=error/401");
        }
        return super.onAccessDenied(request, response, mappedValue);
    }

    @Override
    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        try {
            super.doFilterInternal(request, response, chain);
        } catch (NestedServletException a) {
            Throwable rootCause = a.getRootCause();
            if (rootCause instanceof UnauthorizedException) {

                LOGGER.error("没有权限", a);
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                int userId = 0;
                try{
                    final SysUser currentUser = tokenManagerUtil.getCurrentUser();
                    if(!Objects.isNull(currentUser)){
                        userId = currentUser.getId();
                    }
                }catch (Exception ignored){
                }

                itLogSecurityService.insert(new TLogSecurity(TLogSecurity.OVERPOWER, userId, NetworkUtil.getIpAddress(httpRequest), LocalDateTime.now(), "越权", "对不起，没有授权", ProcessUtil.getPid(), null));

                //ajax则返回json
                if (AjaxUtil.isAjax(httpRequest)) {
                    Result result = new Result(Result.UN_AUTHORIZED, "对不起，没有授权", null);
                    writeJsonError(httpResponse, result);
                } else {//设置授权失败拦截页面
                    super.setUnauthorizedUrl("/common/forward?pagePath=error/401");
                }
            } else if (rootCause instanceof UnauthenticatedException) {
                LOGGER.error("没有登录", a);
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                itLogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, 0, NetworkUtil.getIpAddress(httpRequest), LocalDateTime.now(), "用户登录", "没有登录", ProcessUtil.getPid(), null));
                //ajax则返回json
                if (AjaxUtil.isAjax(httpRequest)) {
                    Result result = new Result(Result.UN_AUTHENTICATION, "对不起，没有登录", null);
                    writeJsonError(httpResponse, result);
                } else {//设置授权失败拦截页面
                    super.setUnauthorizedUrl("/common/forward?pagePath=error/401");
                }
            } else {
                if (AjaxUtil.isAjax((HttpServletRequest) request)) {
                    Result result = new Result(Result.SERVER_ERROR, "系统内部错误", null);
                    writeJsonError((HttpServletResponse) response, result);
                } else {
                    WebUtils.issueRedirect(request, response, "/common/forward?pagePath=error/500");
                }
            }
        }
    }


    /**
     * 往客户端写回json格式的错误信息
     * @param httpResponse
     * @param result
     */
    private void writeJsonError(HttpServletResponse httpResponse, Result result){
        httpResponse.setContentType("application/json;charset=UTF-8");
        httpResponse.setCharacterEncoding("UTF-8");
        try (PrintWriter ou = httpResponse.getWriter()) {
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonStr = objectMapper.writeValueAsString(result);
            ou.print(jsonStr);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

}
